/*******************************************************************************
 * Copyright (c) 2004, 2007 Mylyn project committers and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/

package org.eclipse.mylyn.rememberthemilk.core.model;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.mylyn.rememberthemilk.core.model.RTMSearchFilter.CompareOperator;

/**
 * Represents a Trac search. A search can have multiple {@link TracSearchFilter}s that all need to match.
 * 
 * @author Steffen Pingel
 */
public class RTMSearch {

	/** Stores search criteria in the order entered by the user. */
	private Map<String, RTMSearchFilter> filterByFieldName = new LinkedHashMap<String, RTMSearchFilter>();


	public RTMSearch(String queryParameter) {
		if (queryParameter != null) {
			fromUrl(queryParameter);	
		}
	}

	public RTMSearch() {
	}

	public void addFilter(String key, String value, CompareOperator operator) {
		RTMSearchFilter filter = filterByFieldName.get(key);
		if (filter == null) {
			filter = new RTMSearchFilter(key);
			filter.setOperator(operator);
			filterByFieldName.put(key, filter);
		}

		filter.addValue(value);
	}

	public void addFilter(RTMSearchFilter filter) {
		filterByFieldName.put(filter.getFieldName(), filter);
	}

	public List<RTMSearchFilter> getFilters() {
		return new ArrayList<RTMSearchFilter>(filterByFieldName.values());
	}

	/**
	 * Returns a Trac query string that conforms to the format defined at
	 * {@link http://projects.edgewall.com/trac/wiki/TracQuery#QueryLanguage}.
	 * 
	 * @return the empty string, if no search order and criteria are defined; a string that starts with &amp;, otherwise
	 */
	public String toQuery() {
		StringBuilder sb = new StringBuilder();
		RTMSearchFilter filter;
		for (Iterator<RTMSearchFilter> filterIt = filterByFieldName.values().iterator(); filterIt.hasNext(); )
		{
			filter = filterIt.next();
			sb.append("(");
			List<String> values = filter.getValues();
			for (Iterator<String> it = values.iterator(); it.hasNext();) {
				sb.append(filter.getFieldName());
				sb.append(":");
				String value = it.next();
				boolean spaceMatched = false;
				if (!value.matches("[^ ]*")) { //should evaluate as true if value contains a space
					sb.append("\"");
					spaceMatched = true;
				}
				
				sb.append(value);
				
				if (spaceMatched) {
					sb.append("\"");
				}
				if (it.hasNext()) {
					sb.append(" ");
					sb.append(filter.getOperator().getQueryValue());
					sb.append(" ");
				}
			}
			sb.append(")");
			if (filterIt.hasNext()) {
				sb.append(" ");
				sb.append(CompareOperator.AND.getQueryValue());
				sb.append(" ");
			}
		}
		return sb.toString();
	}

	// .*?\((.*?)\)
	
	//(.*?):(.*?)(\sAND\s|\sOR\s|$)
	
	private static final String MATCH_GROUPS = ".*?\\((.*?)\\)";
	private static final Pattern PATTERN_GROUPS = Pattern.compile(MATCH_GROUPS);
	private static final String MATCH_KEY_VALUE = "(.*?):(.*?)(\\sAND\\s|\\sOR\\s|$)";
	private static final Pattern PATTERN_KEY_VALUE = Pattern.compile(MATCH_KEY_VALUE);
	
	public void fromUrl(String url) {
		
		Matcher m = PATTERN_GROUPS.matcher(url);
		while (m.find())
		{
			String groupContent = m.group(1);
			
			Matcher m_kv = PATTERN_KEY_VALUE.matcher(groupContent);
			while (m_kv.find())
			{
				String key = m_kv.group(1).trim();
				String value = m_kv.group(2).trim();
				if (value.indexOf("\"") == 0 && value.lastIndexOf("\"") == value.length() - 1	){
					value = value.substring(1, value.length() - 1);
				}
				String operator = m_kv.group(3).trim();
				
				CompareOperator compOperator = CompareOperator.fromUrl(operator);
				addFilter(key, value, compOperator);
				
			}
			
		}
	}

}
